package com.cetc.ga.httpclient;

import com.alibaba.fastjson.JSONObject;

import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

public class HttpsUtils {
    public static void main(String[] args) {
        //String url = "http://localhost:8080/user/testJson";
        String url = "https://localhost:8443/user/person";
        //Map<String, String> paramMap = new HashMap<>();
        //paramMap.put("cardId","230306198610245317");
        //paramMap.put("token","123456");

        //String paramstring = JSONObject.fromObject(paramMap).toString();
        //String _result = httpGetRequest(url, paramMap);
        //String _result = httpPostRequest(url, paramMap);
        //String _result = httpsPostRequestJson(url, paramMap);

        String _result = httpsGetRequest(url);
        System.out.println(_result);
    }

    private static SSLSocketFactory initX509TrustManager() {
        //创建SSLContext对象，并使用我们指定的信任管理器初始化
        TrustManager[] tm = {new X509TrustManagerImpl()};
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL","SunJSSE");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        }
        try {
            sslContext.init(null, tm, new java.security.SecureRandom());
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        //从上述SSLContext对象中得到SSLSocketFactory对象
        SSLSocketFactory ssf = sslContext.getSocketFactory();

        //URL reqURL = new URL("https://www.sun.com" ); //创建URL对象
        //创建HttpsURLConnection对象，并设置其SSLSocketFactory对象
        //HttpsURLConnection httpsConn = (HttpsURLConnection)reqURL.openConnection();
        //httpsConn.setSSLSocketFactory(ssf);

        return ssf;
    }

    /**
     * Https Client封装的Https请求
     * @param requestType 请求方法，有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT 这几种
     * @param contentType 提交数据的方式
     * @param url 请求目标url  https://ip:port/xxx
     * @param paramMap 请求参数
     * @param headers 请求头
     * @return 返回请求的结果
     */
    public static String httpsRequest(String requestType, String contentType, String url, Map<String, String> paramMap, Map<String, String> headers) {
        String _result = "";
        /**
         * HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT 这几种。
         * requestType
         */
        switch (requestType) {
            case "GET":
                _result = httpsGetRequest(url, paramMap);
                break;
            case  "POST":
                /**
                 * POST 一般用来向服务端提交数据，POST 提交数据的常见4种方式。
                 */
                if(contentType.equals("application/x-www-form-urlencoded")) {
                    // 补充

                }else if(contentType.equals("multipart/form-data")) {
                    // 补充

                }else if(contentType.equals("application/json")) {
                    _result = httpsPostRequestJson(url,paramMap, headers);
                }else if(contentType.equals("text/xml")) {
                    // 补充

                }else {  // 后面有的话继续补充
                    //System.out.println("");
                    _result = httpsPostRequest(url, paramMap);
                }
                break;
            case "PUT":
                // 补充
                break;
            case "DELETE":
                // 补充
                break;
            case "OPTIONS":
                // 补充
                break;
            case "HEAD":
                // 补充
                break;
            case "TRACE":
                // 补充
                break;
            case "CONNECT":
                // 补充
                break;
            default:
                System.out.println("请求方法不对！！！ HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT 。");
                break;
        }
        return _result;
    }

    /**
     * GET
     */

    /**
     * Https Client封装的Https请求， 请求方法GET
     * @param urlstring 请求目标url  https://ip:port/xxx
     * @param paramMap 请求参数
     * @return
     */
    public static String httpsGetRequest(String urlstring, Map<String, String> paramMap) {
        String paramstring = "";
        for(Map.Entry<String, String> entry : paramMap.entrySet()) {
            paramstring += entry.getKey() + "=" + entry.getValue() + "&";
        }
        paramstring = paramstring.substring(0, paramstring.length()-1);
        urlstring += "?" + paramstring;
        //System.out.println(paramstring);
        return httpsGetRequest(urlstring);
    }

    /**
     * Https Client封装的Https请求， 请求方法GET
     * @param urlstring 请求目标url ,https://ip:port/xxx
     * @param params
     *      请求参数，请求参数应该是
     *      "name1=value1"
     *      "name2=value2"
     *      的形式。
     * @return
     */
    public static String httpsGetRequest(String urlstring, String... params) {
        String param = packageParam(params);
        urlstring += "?" + param;
        return httpsGetRequest(urlstring);
    }

    /**
     * Https Client封装的Https请求， 请求方法GET
     * @param urlstring 请求目标url ,包括请求参数 https://ip:port/xxx?param1=""&param2=""
     * @return
     */
    public static String httpsGetRequest(String urlstring) {
        //urlstring += "?" + paramstring;
        HttpsURLConnection connection = null;
        SSLSocketFactory ssf = null;
        try {
            URL url = new URL(urlstring); //创建URL对象
            //创建HttpsURLConnection对象，并设置其SSLSocketFactory对象
            connection = (HttpsURLConnection) url.openConnection();
            ssf = initX509TrustManager();
            connection.setSSLSocketFactory(ssf);

            connection.setRequestMethod("GET");
            // get response
            InputStream is = connection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuffer response = new StringBuffer();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
                response.append("\t");
            }
            reader.close();
            return response.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * POST
     */

    /**
     * Https Client封装的Https请求， 请求方法Post
     * @param urlstring  请求目标url
     * @param paramMap  请求参数
     * @return
     */
    public static String httpsPostRequest(String urlstring, Map<String, String> paramMap) {
        //String paramstring = JSONObject.fromObject(paramMap).toString();
        String paramstring = "";
        for(Map.Entry<String, String> entry : paramMap.entrySet()) {
            paramstring += entry.getKey() + "=" + entry.getValue() + "&";
        }
        paramstring = paramstring.substring(0, paramstring.length()-1);
        urlstring += "?" + paramstring;
        //System.out.println("url = " + urlstring);
        return httpsPostRequest(urlstring);
    }

    /**
     * Https Client封装的Https请求， 请求方法Post
     * @param urlstring  请求目标url,包括请求参数 https://ip:port/xxx?param1=""&param2=""
     * @return
     */
    public static String httpsPostRequest(String urlstring) {
        HttpsURLConnection connection = null;
        SSLSocketFactory ssf = null;
        try {
            URL url = new URL(urlstring); //创建URL对象
            //创建HttpsURLConnection对象，并设置其SSLSocketFactory对象
            connection = (HttpsURLConnection) url.openConnection();
            ssf = initX509TrustManager();
            connection.setSSLSocketFactory(ssf);

            connection.setRequestMethod("POST");

            connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");

            connection.setRequestProperty("charset", "UTF-8");
            connection.setUseCaches(false);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setConnectTimeout(1000);
            // send request
            /*
            DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
            // wr.writeBytes(paramstring);
            wr.write(paramstring.getBytes());
            //  wr.write(new String(paramstring.getBytes("GBK"),"UTF-8").replaceAll("\"", "'").getBytes());
            //wr.writeChars(paramstring);

            wr.flush();
            wr.close();
            */
            // get response
            InputStream is = connection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuffer response = new StringBuffer();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
                response.append("\t");
            }
            reader.close();
            return response.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Https Client封装的Https请求， 请求方法Post
     * @param urlstring 请求目标url
     * @param paramMap 请求参数,    JSONObject字符串
     * @return
     */
    public static String httpsPostRequestJson(String urlstring, Map<String, String> paramMap) {
        //JSONObject.fromObject(paramMap).toString();
        String paramstring = JSONObject.toJSONString(paramMap);
        //System.out.println(paramstring);
        //return "";
        return httpsPostRequestJson(urlstring, paramstring);
    }

    /**
     * Https Client封装的Https请求， 请求方法Post
     * @param urlstring 请求目标url
     * @param paramstring 请求参数, JSON字符串
     * @return
     */
    public static String httpsPostRequestJson(String urlstring, String paramstring) {
        HttpsURLConnection connection = null;
        SSLSocketFactory ssf = null;
        try {
            URL url = new URL(urlstring); //创建URL对象
            //创建HttpsURLConnection对象，并设置其SSLSocketFactory对象
            connection = (HttpsURLConnection) url.openConnection();
            ssf = initX509TrustManager();
            connection.setSSLSocketFactory(ssf);

            connection.setRequestMethod("POST");

            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");

            connection.setRequestProperty("charset", "UTF-8");
            connection.setUseCaches(false);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setConnectTimeout(1000);
            // send request
            DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
            // wr.writeBytes(paramstring);
            //wr.write(paramstring.getBytes());
            //wr.write(new String(paramstring.getBytes("GBK"),"UTF-8").replaceAll("\"", "'").getBytes());
            wr.write(new String(paramstring.getBytes("GBK"),"UTF-8").getBytes());
            //wr.writeChars(paramstring);

            wr.flush();
            wr.close();

            // get response
            InputStream is = connection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuffer response = new StringBuffer();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
                response.append("\t");
            }
            reader.close();
            return response.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Https Client封装的Https请求， 请求方法Post
     * @param urlstring 请求目标url
     * @param paramMap 请求参数
     * @param headers 请求头
     * @return
     */
    public static String httpsPostRequestJson(String urlstring,  Map<String, String> paramMap, Map<String, String> headers) {
        String paramstring = JSONObject.toJSONString(paramMap);
        return httpsPostRequestJson(urlstring, paramstring, headers);
    }

    /**
     * Https Client封装的Https请求， 请求方法Post
     * @param urlstring  请求目标url
     * @param paramstring  请求参数
     * @param headers  请求头
     * @return
     */
    public static String httpsPostRequestJson(String urlstring, String paramstring, Map<String, String> headers) {
        HttpsURLConnection connection = null;
        SSLSocketFactory ssf = null;
        try {
            URL url = new URL(urlstring); //创建URL对象
            //创建HttpsURLConnection对象，并设置其SSLSocketFactory对象
            connection = (HttpsURLConnection) url.openConnection();
            ssf = initX509TrustManager();
            connection.setSSLSocketFactory(ssf);

            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
            connection.setRequestProperty("charset", "UTF-8");
            if (headers != null) {
                for (Map.Entry<String, String> header : headers.entrySet()) {
                    connection.setRequestProperty(header.getKey(), header.getValue());
                }
            }

            connection.setUseCaches(false);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setConnectTimeout(1000);
            // send request
            DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
            // wr.writeBytes(paramstring);
            wr.write(paramstring.getBytes());
            wr.flush();
            wr.close();
            // get response
            InputStream is = connection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuffer response = new StringBuffer();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
                response.append("\t");
            }
            reader.close();
            return response.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将参数封装成我们需要的形式
     * @param params
     *   参数形式是
     *   name1=value1
     *   name2=value2
     * @return 返回的请求参数应该是 name1=value1&name2=value2的形式
     */
    private static String packageParam(String[] params) {
        String pre = "_zcargs";
        String param = "";
        int k = 0;
        for(int i=0; i<params.length; i++) {
            if(params[i].contains("=")) {
                if(countChar(params[i],'=') == 1 ) {
                    if(params[i].indexOf('=') == 0)
                        param += pre + (k++) + params[i] + "&";
                    else
                        param += params[i] + "&";
                } else {
                    param += params[i] + "&";
                }
            } else {
                param += pre + (k++) + "=" + params[i] + "&";
            }
        }
        param = param.substring(0, param.length()-1);
        return param;
    }

    /**
     * 计数某个字符在字符串中出现的次数
     * @param str 字符串
     * @param ch 字符
     * @return  某个字符在字符串中出现的次数
     */
    private static int countChar(String str,char ch) {
        // 将字符串转换为字符数组
        char[] chs = str.toCharArray();
        // 定义变量count存储字符串出现次数
        int count = 0;
        for(int i = 0;i < chs.length;i++) {
            if(chs[i] == ch) {
                count++;
            }
        }
        return count;
    }
}

/**
 * 参考：https://www.cnblogs.com/zhangchao0515/p/10455219.html
 * https://www.cnblogs.com/chinway/p/5802541.html
 * X509证书信任管理器类的实现及应用。
 *
 * 在JSSE中，证书信任管理器类就是实现了接口X509TrustManager的类。
 * 我们可以自己实现该接口，让它信任我们指定的证书。
 *
 * 接口X509TrustManager有下述三个公有的方法需要我们实现
 */
class X509TrustManagerImpl implements X509TrustManager {

    /**
     * 该方法检查客户端的证书，若不信任该证书则抛出异常。由于我们不需要对客户端进行认证，
     * 因此我们只需要执行默认的信任管理器的这个方法。JSSE中，默认的信任管理器类为TrustManager。
     * @param x509Certificates
     * @param s
     * @throws CertificateException
     */
     @Override
     public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

     }

    /**
     * 该方法检查服务器的证书，若不信任该证书同样抛出异常。通过自己实现该方法，可以使之信任我们指定的任何证书。
     * 在实现该方法时，也可以简单的不做任何处理，即一个空的函数体，由于不会抛出异常，它就会信任任何证书。
     * @param x509Certificates
     * @param s
     * @throws CertificateException
     */
     @Override
     public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

     }

    /**
     * 返回受信任的X509证书数组。
     * @return
     */
     @Override
     public X509Certificate[] getAcceptedIssuers() {
         return new X509Certificate[0];
     }
 }